package com.zds.passwordbox.data.dao;

import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import androidx.annotation.NonNull;

import com.zds.passwordbox.common.Constant;
import com.zds.passwordbox.common.util.CryptoUtil;
import com.zds.passwordbox.data.dao.domain.UserAccount;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import javax.crypto.spec.SecretKeySpec;

public class AccountDao {

    private final SQLiteDatabase db;

    private static volatile AccountDao instance;

    public AccountDao(SQLiteDatabase db) {
        this.db = db;
    }

    public static synchronized AccountDao getInstance(SQLiteDatabase db) {
        if (instance == null) {
            instance = new AccountDao(db);
        }
        return instance;
    }

    public void insert(UserAccount account) {
        account.setId(null);
        account.setUpdateTime(System.currentTimeMillis() / 1000);
        ContentValues values = getContentValues(account, false);
        //参数依次是：表名，插入null值的数据列的列名，数据
        db.insert(Constant.ACCOUNT_TABLE, null, values);
    }

    public void encryptedInsert(UserAccount account) {
        account.setId(null);
        account.setUpdateTime(System.currentTimeMillis() / 1000);
        ContentValues values = getContentValues(account, true);
        //参数依次是：表名，插入null值的数据列的列名，数据
        db.insert(Constant.ACCOUNT_TABLE, null, values);
    }

    public void delete(Long id) {
        db.delete(Constant.ACCOUNT_TABLE, "id = ?", new String[]{id.toString()});
    }

    public void update(UserAccount account) {
        account.setUpdateTime(System.currentTimeMillis() / 1000);
        ContentValues values = getContentValues(account, true);
        //参数依次是表名，修改后的值，where条件，以及约束，如果不指定三四两个参数，会更改所有行
        db.update(Constant.ACCOUNT_TABLE, values,
                "id = ?", new String[]{account.getId().toString()});
    }

    public LinkedList<UserAccount> queryList(Integer accountType, String keyword) {
        LinkedList<UserAccount> dataList = new LinkedList<>();
        String selection = null;
        String[] selectionParams = null;

        // 处理查询条件
        if (accountType != null && (keyword != null && !keyword.trim().isEmpty())) {
            selection = "type = ? and (typeText like ? or username like ?)";
            selectionParams = new String[]{accountType.toString(),
                    "%" + keyword + "%", "%" + keyword + "%"};
        } else if (keyword != null && !keyword.trim().isEmpty()) {
            selection = "typeText like ? or username like ?";
            selectionParams = new String[]{"%" + keyword + "%", "%" + keyword + "%"};
        } else if (accountType != null) {
            selection = "type = ?";
            selectionParams = new String[]{accountType.toString()};
        }
        //创建游标对象
        Cursor cursor = db.query(Constant.ACCOUNT_TABLE,
                new String[]{"id", "typeText", "username", "secret", "type"},
                selection, selectionParams,
                null, null, "updateTime DESC");
        //利用游标遍历所有数据对象
        if (cursor.moveToFirst()) {
            do {
                int id = cursor.getInt(cursor.getColumnIndexOrThrow("id"));
                String typeText = cursor.getString(cursor.getColumnIndexOrThrow("typeText"));
                String username = cursor.getString(cursor.getColumnIndexOrThrow("username"));
                String secret = cursor.getString(cursor.getColumnIndexOrThrow("secret"));
                Integer type = cursor.getInt(cursor.getColumnIndexOrThrow("type"));

                dataList.add(new UserAccount((long) id, typeText, username, secret, type));
            } while (cursor.moveToNext());
        }
        // 关闭游标，释放资源
        cursor.close();
        return dataList;
    }

    public UserAccount queryOne(Long id) {
        UserAccount account = new UserAccount();
        if (id == null) {
            return new UserAccount();
        }
        String selection = "id = ?";
        String[] selectionParams = new String[]{id.toString()};
        //创建游标对象
        Cursor cursor = db.query(Constant.ACCOUNT_TABLE,
                new String[]{"id", "typeText", "username", "secret", "type"},
                selection, selectionParams,
                null, null, null);
        //利用游标遍历所有数据对象
        if (cursor.moveToFirst()) {
            int id1 = cursor.getInt(cursor.getColumnIndexOrThrow("id"));
            String typeText = cursor.getString(cursor.getColumnIndexOrThrow("typeText"));
            String username = cursor.getString(cursor.getColumnIndexOrThrow("username"));
            String secret = cursor.getString(cursor.getColumnIndexOrThrow("secret"));
            Integer type = cursor.getInt(cursor.getColumnIndexOrThrow("type"));
            // 对secret解密
            String userKey = PrivateKeyDao.getInstance(db).getUserKey();
            SecretKeySpec secretKeySpec = CryptoUtil.generateFixedKey(userKey);
            String decrypt;
            try {
                decrypt = CryptoUtil.decrypt(secretKeySpec, secret);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            account = new UserAccount(id, typeText, username, decrypt, type);
        }
        // 关闭游标，释放资源
        cursor.close();
        return account;
    }

    public List<String> queryUsernames(String keyword) {
        List<String> dataList = new ArrayList<>();
        String selection = null;
        String[] selectionParams = null;

        // 处理查询条件
        if (keyword != null && !keyword.trim().isEmpty()) {
            selection = "username like ?";
            selectionParams = new String[]{"%" + keyword + "%"};
        }
        //创建游标对象
        Cursor cursor = db.query(Constant.ACCOUNT_TABLE,
                new String[]{"username"},
                selection, selectionParams,
                null, null, "updateTime DESC");
        //利用游标遍历所有数据对象
        if (cursor.moveToFirst()) {
            do {
                String username = cursor.getString(cursor.getColumnIndexOrThrow("username"));
                if (!dataList.contains(username)) {
                    dataList.add(username);
                }
            } while (cursor.moveToNext());
        }
        // 关闭游标，释放资源
        cursor.close();
        return dataList;
    }

    public boolean accountExist (String typeText, String username, Integer type) {
        if (typeText == null || username == null || type == null) {
            return false;
        }
        String selection = "typeText = ? and username = ? and type = ?";
        String[] selectionParams = new String[]{typeText, username, type.toString()};
        //创建游标对象
        Cursor cursor = db.query(Constant.ACCOUNT_TABLE,
                new String[]{"id", "typeText", "username", "secret", "type"},
                selection, selectionParams,
                null, null, null);
        //利用游标遍历所有数据对象
        if (cursor.moveToFirst()) {
            // 关闭游标，释放资源
            cursor.close();
            return true;
        }
        // 关闭游标，释放资源
        cursor.close();
        return false;
    }

    @NonNull
    private ContentValues getContentValues(UserAccount account, boolean encrypt) {
        ContentValues values = new ContentValues();
        values.put("id", account.getId());
        values.put("typeText", account.getTypeText());
        values.put("username", account.getUsername());
        if (encrypt) {
            // 对账户密码进行加密
            values.put("secret", encryptSecret(account.getSecret()));
        } else {
            values.put("secret", account.getSecret());
        }
        values.put("type", account.getType());
        values.put("updateTime", account.getUpdateTime());
        return values;
    }

    private String encryptSecret(String password) {
        String userKey = PrivateKeyDao.getInstance(db).getUserKey();
        SecretKeySpec secretKeySpec = CryptoUtil.generateFixedKey(userKey);
        String encrypt;
        try {
            encrypt = CryptoUtil.encrypt(secretKeySpec, password.getBytes(CryptoUtil.CHARSET_UTF8));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return encrypt;
    }
}
